📌 گام ۱.۱: افزودن تنظیمات قابل پیکربندی
در کلاس XIdentityConfiguration (موجود در xIdentityModels)، بخش جدید برای تنظیمات ApiKey اضافه میشود:
public class XIdentityConfiguration
{
public XApiKeyConfiguration ApiKey { get; set; } = new();
}
public class XApiKeyConfiguration
{
public int DefaultExpirationMinutes { get; set; } = 43200;
public int MaxExpirationMinutes { get; set; } = 525600;
public int MaxKeysPerApplication { get; set; } = 10;
public int NotifyBeforeMinutes { get; set; } = 10080;
public bool EnableAuditLog { get; set; } = true;
public int DefaultRateLimit { get; set; } = 60;
}
📌 گام ۱.۲: افزودن DbContext و Migration
در XIdentityDbContext، DbSetهای جدید اضافه میشوند:
public class XIdentityDbContext : IdentityDbContext
{
public DbSet<XApplication> Applications { get; set; }
public DbSet<XApiKey> ApiKeys { get; set; }
public DbSet<XApiKeyUsageLog> ApiKeyUsageLogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<XApplication>(e => {
e.HasIndex(a => a.Name).IsUnique();
e.HasMany(a => a.ApiKeys)
.WithOne(k => k.Application)
.HasForeignKey(k => k.ApplicationId);
});
modelBuilder.Entity<XApiKey>(e => {
e.HasIndex(k => k.KeyHash).IsUnique();
e.HasIndex(k => k.ExpiresAt);
});
}
}
📌 گام ۱.۳: پیادهسازی XApplicationManager
یک کلاس جدید برای مدیریت برنامهها و ApiKeyها مشابه الگوی XIdentityManager موجود:
public interface IXApplicationManager
{
Task<XApplicationDto> CreateApplication(XApplicationDto item, string ownerId);
Task<XApplicationDto> UpdateApplication(Guid id, XApplicationDto item);
Task<bool> DeleteApplication(Guid id);
Task<XApplicationDto> GetApplication(Guid id);
Task<IEnumerable<XApplicationDto>> GetOwnerApplications(string ownerId);
Task<XApiKeyCreationResult> CreateApiKey(
Guid applicationId,
TimeSpan? expiration = null,
IEnumerable<string> scopes = null,
IEnumerable<string> allowedIPs = null,
int? rateLimit = null);
Task<bool> RevokeApiKey(Guid apiKeyId, string revokedBy);
Task<XApiKeyDto> RotateApiKey(Guid apiKeyId, TimeSpan? newExpiration = null);
Task<IEnumerable<XApiKeyDto>> GetApplicationApiKeys(Guid applicationId);
Task<XApiKeyValidationResult> ValidateApiKey(string apiKey, string clientIP);
}
📌 گام ۱.۴: تولید امن ApiKey
الگوی تولید کلید باید از نظر رمزنگاری امن باشد:
public static class XApiKeyGenerator
{
public static (string plainKey, string hash, string prefix) Generate()
{
var randomBytes = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
var plainKey = $"xapp_{Convert.ToBase64String(randomBytes)
.Replace("+", "-").Replace("/", "_").TrimEnd('=')}";
using (var sha256 = SHA256.Create())
{
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plainKey));
var hash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
var prefix = plainKey.Substring(0, 16) + "...";
return (plainKey, hash, prefix);
}
}
public static bool Verify(string plainKey, string storedHash)
{
using (var sha256 = SHA256.Create())
{
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plainKey));
var computedHash = BitConverter.ToString(hashBytes)
.Replace("-", "").ToLower();
return computedHash == storedHash;
}
}
}
✅ ویژگیهای امنیتی این طراحی:
- کلید ApiKey هرگز بهصورت Plain Text ذخیره نمیشود (فقط Hash)
- استفاده از
RandomNumberGenerator برای تولید امن
- پیشوند
xapp_ برای شناسایی سریع نوع کلید
- Prefix کوتاه برای نمایش در UI بدون افشای کلید کامل